﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Kugar.Core.Security.Permission;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using Kugar.Core.ExtMethod;


namespace Kugar.Core.Security.Permission
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
    public class PermissionCheckAttribute : HandlerAttribute
    {
        public PermissionCheckAttribute()
        {
            IsThrowExceptionWhenUnChecked = false;
            //base.Order = 1;
        }

        public Type Handler { set; get; }

        public ICallHandler CallHander { set; get; }

        public bool IsThrowExceptionWhenUnChecked { set; get; }

        public string OnPermissionChecked { set; get; }

        public string OnBeginInvoke { set; get; }

        public string OnEndInvoke { set; get; }

        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            if (CallHander!=null)
            {
                return CallHander;
            }

            ICallHandler callHander = null;

            if (Handler!=null)
            {
                callHander= (ICallHandler)Activator.CreateInstance(Handler);
            }
            else
            {
                callHander= new PermissionCallHandler()
                                {
                                        BeginInvoke = this.OnBeginInvoke,
                                        EndInvoke = this.OnEndInvoke,
                                        PermissionChecked = this.OnPermissionChecked,

                                };
                
            }

            return callHander;
        }


    }

    public class PermissionCallHandler : ICallHandler
    {
        private static readonly Type checkAttribute = typeof(PermissionInfoAttribute);

        public string PermissionChecked { set; get; }

        public string BeginInvoke { set; get; }

        public string EndInvoke { set; get; }

        protected EventHandler<PermissionCheckedEventArgs> PermissionCheckedEvent { set; private get; }

        protected EventHandler<PermissionCheckedEventArgs> BeginInvokeCallback { set; private get; }

        protected EventHandler<PermissionCheckedEventArgs> EndInvokeCallback { set; private get; }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var hasPermission = false;

            PermissionCheckedEventArgs e = null;

            var piList = (PermissionInfoAttribute[])input.MethodBase.GetCustomAttributes(checkAttribute, true);

            if (piList==null || piList.Length<=0)
            {
                return getNext().Invoke(input, getNext);
            }

            PermissionInfoAttribute lastErrorAction = null;

            do
            {
                var errorAction = CheckPermission(piList);

                hasPermission =errorAction==null ;

                if (!hasPermission)
                {
                    if (e == null)
                    {
                        e = new PermissionCheckedEventArgs("", errorAction, new NoPermissionException(errorAction));
                    }

                    e.IsRetryCheck = false;

                    if (errorAction.Equals(lastErrorAction))
                    {
                        e.CheckTimes++;
                    }
                    else
                    {
                        e.CheckTimes = 1;
                        lastErrorAction = errorAction;
                    }

                    OnPermissionChecked(input.Target, e);

                }

            } while (!hasPermission && (e != null && e.IsRetryCheck));

            if (!hasPermission)
            {
                return input.CreateMethodReturn(null, input.Arguments);
            }

            if (BeginInvoke != null)
            {
                input.Target.FastInvoke(BeginInvoke, this, new PermissionBeginInvokeEventArgs() { Params = input.Inputs });
            }

            var ret = getNext().Invoke(input, getNext);

            if (EndInvoke != null)
            {
                var endEventArgs = new PermissionEndInvokeEventArgs()
                {
                    ReturnValue = ret.ReturnValue,
                    Error = ret.Exception,
                    Outputs = ret.Outputs
                };

                input.Target.FastInvoke(EndInvoke, this, endEventArgs);

                ret.ReturnValue = endEventArgs.ReturnValue;

                ret.Exception = endEventArgs.Error;

            }

            return ret;
        }

        public int Order { get; set; }

        protected void OnPermissionChecked(object target, PermissionCheckedEventArgs e)
        {
            if (!PermissionChecked.IsEmptyOrWhileSpace())
            {
                target.FastInvoke("OnPermissionChecked", this, e);
                //OnPermissionChecked(this, e);
            }
            else if (PermissionCheckedEvent !=null)
            {
                PermissionCheckedEvent(this, e);
            }
            else
            {
                throw new NoPermissionException(e.PermissionInfo);
            }
        }

        //检查权限,并返回第一个无法检查通过的权限
        private PermissionInfoAttribute CheckPermission(PermissionInfoAttribute[] piList)
        {
            if (piList==null || piList.Length<=0)
            {
                return null;
            }

            var p = PermissionManage.GetCurrentUserPermission();

            if (p == null)
            {
                return new PermissionInfoAttribute(){ActionName = "sdfsdf"};
            }

            for (int i = 0; i < piList.Length; i++)
            {
                var item = piList[i];

                var isEnable=p.HasPermission(item.ActionName);

                if (!isEnable)
                {
                    return item;
                }
            }

            return null;
        }
    }

}
